#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include "uti_broadcast_open.h"
#include "uti_consolecolor.h"

const unsigned int SEMRECORD_MAPSIZE = 50;

BroadcastOpen::BroadcastOpen(BarInfo* pbar)
{
	memcpy(&m_barinfo, pbar, sizeof(BarInfo));
	memset(m_pdrivemqindex_client, 0, sizeof(m_pdrivemqindex_client));
	
	
	unsigned int size = sizeof(m_shmarr)/sizeof(SharedMemory);
	for (unsigned int i = 0; i < size; i++)
	{
		unsigned sersize  = 0;
		char* mqsuffix = NULL;
		char* semrecsuffix = NULL;
		if (0 == i)
		{
			sersize = DEFAULT_TICKSERSIZE_OFDAY;
			mqsuffix = "tickmq";
			semrecsuffix = "ticksem";
		}		
		else if (1 == i)
		{
			sersize = DEFAULT_SECSERSIZE_OFDAY;;
			mqsuffix = "secmq";
			semrecsuffix = "secsem";
		}
		else if (2 == i)
		{
			sersize = DEFAULT_MINSERSIZE_OFDAY;
			mqsuffix = "minmq";
			semrecsuffix = "minsem";
		}
		else if (3 == i)
		{
			sersize = DEFAULT_DAYSERSIZE_OFDAY;
			mqsuffix = "daymq";
			semrecsuffix = "daysem";
		}

		if (false == OpenSharedMQ(i, sersize, mqsuffix))
		{
			pantheios::log_ERROR("[BroadcastOpen] OpenSharedMQ ", m_barinfo.instru, ":", mqsuffix, " failed");
			return;
		}

		if (false == OpenSharedSemRecord(i, semrecsuffix))
		{
			pantheios::log_ERROR("[BroadcastOpen] OpenSharedSemRecord ", m_barinfo.instru, ":", semrecsuffix, " failed");
			return;
		}

	}

	if (false == OpenSharedMutex())
	{
		pantheios::log_ERROR("[BroadcastOpen] OpenSharedMutex failed");
		return;
	}

}

BroadcastOpen::~BroadcastOpen()
{
	unsigned int size = sizeof(m_shmarr)/sizeof(SharedMemory);
	for (unsigned int i = 0; i < size; i++)
	{
		DestroySharedMQ(i);


		DestroySharedSemRecord(i);

	}

	DestroySharedMutex();
}



bool BroadcastOpen::GetSecurityAttributes(SECURITY_ATTRIBUTES &securityAttributes)
{
	//SECURITY_ATTRIBUTES securityAttributes;
	//char secDesc[SECURITY_DESCRIPTOR_MIN_LENGTH];
	securityAttributes.nLength = sizeof(securityAttributes);
	securityAttributes.bInheritHandle = FALSE;
	//securityAttributes.lpSecurityDescriptor = &secDesc;

	if (!InitializeSecurityDescriptor(securityAttributes.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION)) 
	{  
		pantheios::log_ERROR("[GetSecurityAttributes] InitializeSecurityDescriptor failed error:", panint(GetLastError()));
		return false;	
	} 

	// Add the ACL to the security descriptor. 
	if (!SetSecurityDescriptorDacl(securityAttributes.lpSecurityDescriptor, TRUE, 0, FALSE))   // not a default DACL 
	{  
		pantheios::log_ERROR("[GetSecurityAttributes] SetSecurityDescriptorDacl failed error:", panint(GetLastError()));
		return false;			
	} 


	//PSECURITY_DESCRIPTOR pSD;
	//ConvertStringSecurityDescriptorToSecurityDescriptorA("S:(ML;;NW;;;LW)", // this means "low integrity"
	//	SDDL_REVISION_1,
	//	&pSD,
	//	NULL);
	//PACL pSacl = NULL;         // not allocated
	//BOOL fSaclPresent = FALSE;
	//BOOL fSaclDefaulted = FALSE;
	//GetSecurityDescriptorSacl(pSD,
	//	&fSaclPresent,
	//	&pSacl,
	//	&fSaclDefaulted);
	//SetSecurityDescriptorSacl(securityAttributes.lpSecurityDescriptor, TRUE, pSacl, FALSE);


	return true;
}


bool BroadcastOpen::OpenSharedMQ(unsigned int index, unsigned int sersize, char* suffix)
{

	unsigned int map_size = sersize * sizeof(BarData) + sizeof(unsigned int);


	char ch[100];
	GetCharName(suffix, ch);

		

	m_shmarr[index].hMapFile_barser = OpenFileMapping(FILE_MAP_ALL_ACCESS,
		FALSE,
		ch);
	if(m_shmarr[index].hMapFile_barser == NULL)
	{
		pantheios::log_ERROR("[OpenSharedMQ] OpenFileMapping failed error:", panint(GetLastError()));
		DestroySharedMQ(index);
		return false;
	}
	pantheios::log_NOTICE("[OpenSharedMQ] The file mapping (", ch, ") is opened");

	m_shmarr[index].pView_barser = MapViewOfFile(m_shmarr[index].hMapFile_barser,
		FILE_MAP_WRITE,
		0,
		0,
		map_size);

	if(m_shmarr[index].pView_barser == NULL)
	{
		pantheios::log_ERROR("[OpenSharedMQ] MapViewOfFile failed error:", panint(GetLastError()));
		DestroySharedMQ(index);
		return false;
	}

	pantheios::log_NOTICE("[OpenSharedMQ] The file view (", ch, ") is mapped");


	m_shmarr[index].pindex_mq = (unsigned int*)m_shmarr[index].pView_barser;

	unsigned int* p = (unsigned int*)m_shmarr[index].pView_barser;
	p++;

	m_shmarr[index].pmq_barser = (BarData*) p;


	return true;
}



void BroadcastOpen::DestroySharedMQ(unsigned int index)
{
	
	if (m_shmarr[index].hMapFile_barser)
	{
		if (m_shmarr[index].pView_barser)
		{
			// Unmap the file view.
			UnmapViewOfFile(m_shmarr[index].pView_barser);
			m_shmarr[index].pView_barser = NULL;
		}
		// Close the file mapping object.
		CloseHandle(m_shmarr[index].hMapFile_barser);
		m_shmarr[index].hMapFile_barser = NULL;
	}
}




bool BroadcastOpen::OpenSharedSemRecord(unsigned int index, char* suffix)
{
	unsigned int map_size = sizeof(semrecord) * SEMRECORD_MAPSIZE + sizeof(unsigned int);

	char ch[100];
	GetCharName(suffix, ch);


	m_shmarr[index].hMapFile_semrec = OpenFileMapping(FILE_MAP_ALL_ACCESS,
		FALSE,
		ch);
	if (m_shmarr[index].hMapFile_semrec == NULL) 
	{
		pantheios::log_ERROR("[OpenSharedSemRecord] OpenFileMapping failed error:", panint(GetLastError()));
		DestroySharedSemRecord(index);
		return false;
	}
	pantheios::log_NOTICE("[OpenSharedSemRecord] The file mapping (", ch, ") is opened");

	// Map a view of the file mapping into the address space of the current process.
	m_shmarr[index].pView_semrec = MapViewOfFile(
		m_shmarr[index].hMapFile_semrec,               // Handle of the map object
		FILE_MAP_ALL_ACCESS,    // Read and write access
		0,                      // High-order DWORD of the file offset 
		0,			            // Low-order DWORD of the file offset 
		map_size   // The number of bytes to map to view
		);
	if (m_shmarr[index].pView_semrec == NULL)
	{ 
		pantheios::log_ERROR("[OpenSharedSemRecord] MapViewOfFile failed error:", panint(GetLastError()));
		DestroySharedSemRecord(index);
		return false;
	}

	pantheios::log_NOTICE("[OpenSharedSemRecord] The file view (", ch, ") is mapped");

	m_shmarr[index].psize_semrec = (unsigned int*)m_shmarr[index].pView_semrec;

	unsigned int* p = (unsigned int*)m_shmarr[index].pView_semrec;
	p++;

	m_shmarr[index].pmq_semrec =  (semrecord*) p;

	return true;
}



void BroadcastOpen::DestroySharedSemRecord(unsigned int index)
{
	///****************Delete han in local enviroment**************************/

	WaitForSingleObject (m_mutex, INFINITE);

	//close sema
	//DestroySharedsema(index);


	//reset semaname
	memset(m_matainsemrec[index].semname, 0, sizeof(m_matainsemrec[index].semname));

	ReleaseMutex(m_mutex);
	///************************************************************************/


	if (m_shmarr[index].hMapFile_semrec)
	{
		if (m_shmarr[index].pView_semrec)
		{
			// Unmap the file view.
			UnmapViewOfFile(m_shmarr[index].pView_semrec);
			m_shmarr[index].pView_semrec = NULL;
		}
		// Close the file mapping object.
		CloseHandle(m_shmarr[index].hMapFile_semrec);
		m_shmarr[index].hMapFile_semrec = NULL;
	}
}



bool BroadcastOpen::OpenSharedMutex()
{
	char ch[100];
	GetCharName("mutex", ch);


	m_mutex = OpenMutex( 
		MUTEX_ALL_ACCESS,            // request full access
		FALSE,                       // handle not inheritable
		ch);  // object name

	if (m_mutex == NULL) 
	{
		pantheios::log_ERROR("[OpenShareMutex] OpenMutex failed error:", panint(GetLastError()));

		return false;
	}

	pantheios::log_NOTICE("[OpenShareMutex] Open a new mutex:", ch);

	return true;	
}


void BroadcastOpen::DestroySharedMutex()
{

	if (m_mutex)
	{

		CloseHandle(m_mutex);
		m_mutex = NULL;
	}
}

//unsigned int m_pmqindex_client;
//HANDLE m_psem;_client;

bool BroadcastOpen::CalculateSemName(unsigned int index)
{
	//get last NOT LEN ZERO semname
	//char* destsemname = NULL;

	unsigned int size = *m_shmarr[index].psize_semrec;


	if (0 != size) //get last semrecord
	{
		char* semname = (m_shmarr[index].pmq_semrec + size - 1)->semname;

		unsigned int len = strlen(semname);

		bool bfound = false;
		unsigned int pos = len - 1;
		for (pos; pos >= 0; pos-- )
		{
			if (semname[pos] == '_')
			{
				bfound = true;
				break;
			}
		}

		char* pnum = NULL;
		if (bfound )
		{
			pnum = semname + pos + 1;
		}
		else
		{
			pantheios::log_ERROR("[CalculateSemName] error not found _");
			return false;
		}
			

		unsigned int inum = atoi(pnum) + 1;


		char ch[100] = {0};

		sprintf(ch, "semname_%d", inum);


		GetCharName(ch, m_matainsemrec[index].semname);

	} 
	else// no semrecord exist
	{
		GetCharName("semname_0", m_matainsemrec[index].semname);
	}

	pantheios::log_WARNING("[CalculateSemName] Get New SemaName:", m_matainsemrec[index].semname);

	return true;
}




bool BroadcastOpen::InitSharedsema(unsigned int index)
{
	SECURITY_ATTRIBUTES securityAttributes;
	char secDesc[SECURITY_DESCRIPTOR_MIN_LENGTH];
	securityAttributes.lpSecurityDescriptor = &secDesc;

	if (!GetSecurityAttributes(securityAttributes))   
	{  
		pantheios::log_ERROR("[InitSharedsema] GetSecurityAttributes failed");
		return false;			
	} 

	m_matainsemrec[index].han = CreateSemaphore(&securityAttributes, 0, 200, m_matainsemrec[index].semname);
	if (m_matainsemrec[index].han == NULL || GetLastError() == ERROR_ALREADY_EXISTS) 
	{
		pantheios::log_ERROR("[InitSharedsema] CreateSemaphore:", m_matainsemrec[index].semname, ", Error:", panint(GetLastError() ));
		return false;
	}

	pantheios::log_WARNING("[InitSharedsema] Created a new sema:", m_matainsemrec[index].semname);

	return true;
	
}

bool BroadcastOpen::Register(Bartype type, HANDLE* phan_out/*out*/, vector<BarSeries*>* pvbarser/*out*/)
{
	unsigned int index = 0;
	if (type == tick)
		index = 0;


	WaitForSingleObject (m_mutex, INFINITE);
	
	
	//Caculate semaphore name in m_matainsemrec[index]
	if (false == CalculateSemName(index))
	{
		ReleaseMutex(m_mutex);
		return false;
	}
	
	//Init Shared semaphore in m_matainsemrec[index]
	if (false == InitSharedsema(index))
	{
		ReleaseMutex(m_mutex);
		return false;
	}
	
	/****************Update shared info (semname and index)**********************/
	//update shared pmq_semrec
	semrecord* paddsemrec = (m_shmarr[index].pmq_semrec + *m_shmarr[index].psize_semrec);

	strcpy(paddsemrec->semname, m_matainsemrec[index].semname);// only consider tick now

	paddsemrec->bactive = true;

	//update shared m_psemrecindex
	(*m_shmarr[index].psize_semrec)++;
	/************************************************************************/



	/*****Set Local variable *****************************************************/

	//*1.set out HANDLE to local HANDLE
	*phan_out = m_matainsemrec[index].han;

	//*2.Set driveindex* //record this variable used for added it receiving server semaphore
	unsigned int size = sizeof(m_shmarr)/sizeof(SharedMemory);
	for (unsigned int i = 0; i < size; i++)
		m_pdrivemqindex_client[i] = *m_shmarr[i].pindex_mq;

	//3.If not First data should release once to avoid data loss
	if (0 != m_pdrivemqindex_client[index])
	{
		long precnt = 0;
		ReleaseSemaphore(m_matainsemrec[index].han, 1, &precnt);
		if (0 != precnt)
			pantheios::log_WARNING("[Register] delay previous semaphore:", m_matainsemrec[index].semname, " cnt is:", panint(precnt));
	}
	/*******************************************************************************/

	/****************Read SHM mq data*******************************************/
	if (pvbarser)
	{
		vector<BarSeries*>::iterator it = pvbarser->begin();
		for (it; it != pvbarser->end(); it++)	//for every Barser user init
		{
			BarSeries* pbarser = *it;
			if (pbarser)
			{
				char* inst = pbarser->m_serinfo.barinfo.instru;

				if (0 == strcmp(m_barinfo.instru, inst))// user may init barser not for drived, so filter them
				{
					unsigned int usertypeindex = pbarser->m_serinfo.barinfo.type - Bartype::tick;	//according user int bar type choose shm mq
					BarData* pmqstart = m_shmarr[usertypeindex].pmq_barser;
					unsigned int rdindex = m_pdrivemqindex_client[usertypeindex];

					for (unsigned int i = 0; i <= rdindex; i++)	//for every BarData in mq
					{
						BarData* prddata = pmqstart + i;

						if (0 != prddata->tickcnt) //avoid first unfinished bar
						{
							pbarser->Addbardata(prddata);
						}


					}

				}
			}

		}
	}



	/************************************************************************/

	ReleaseMutex(m_mutex);


	return true;
}



bool BroadcastOpen::DestroySharedsema(unsigned int index)
{

	if (m_matainsemrec[index].han)
	{
		if (CloseHandle(m_matainsemrec[index].han))
		{
			pantheios::log_WARNING("[DestroySharedsema] Destroy sema:", m_matainsemrec[index].semname, " successed");
			m_matainsemrec[index].han = NULL;
		
		}	
		else
		{
			pantheios::log_ERROR("[DestroySharedsema] Destroy sema:", m_matainsemrec[index].semname, " failed");
			m_matainsemrec[index].han = NULL;

			return false;
		}
			
		
	}

	return true;

}

bool BroadcastOpen::UnRegister(Bartype type)
{
	unsigned int index = 0;
	if (type == tick)
		index = 0;

	WaitForSingleObject (m_mutex, INFINITE);

	/****************Delete han in local enviroment**************************/
	//close sema
	if (false == DestroySharedsema(index))
	{
		ReleaseMutex(m_mutex);
		return false;
	}

	//reset semaname
	char backname[50] = {0};

	strcpy(backname, m_matainsemrec[index].semname);

	memset(m_matainsemrec[index].semname, 0, sizeof(m_matainsemrec[index].semname));
	/************************************************************************/


	/****************Update shared info to empty semname**********************/
	//update shared pmq_semrec
	semrecord* pstart = (m_shmarr[index].pmq_semrec);
	unsigned int semcnt = *m_shmarr[index].psize_semrec;
	unsigned int i;
	for (i = 0; i < semcnt; i++)
	{
		if (0 == strcmp(pstart[i].semname, backname))
			break;
	}
	if (i == semcnt)
	{
		pantheios::log_ERROR("[UnRegister] not found:", backname);

		ReleaseMutex(m_mutex);
		return false;
	}

	pstart[i].bactive = false;
	/************************************************************************/



	
	ReleaseMutex(m_mutex);

	return true;
}





void BroadcastOpen::GetCharName(const char* pname, char* pchar)
{
	string prefix = SHARENAME_PREFIX;
	prefix += m_barinfo.instru;
	prefix += "_";
	string fullname = prefix + pname;
	strcpy(pchar, fullname.c_str());
	
}







//void BroadcastOpen::BroadcastMessage(CThostFtdcDepthMarketDataField *pDepthMarketData)
//{
//	//insert tick into large mq
//
//	memcpy(m_pMarket, pDepthMarketData, sizeof(CThostFtdcDepthMarketDataField));
//
//	m_pMarket++;
//
//	
//	//release semaphore for every strategy thread
//
//	unsigned int semNamecnt = m_pclientcfg->semNamecnt;
//
//	for (unsigned int index = 0; index < semNamecnt; index++)
//	{
//		/*if (m_hSem[index])
//		CloseHandle(m_hSem[index]);*/
//
//		long precnt = 0;
//		ReleaseSemaphore(m_hSem[index], 1, &precnt);
//
//		if (0 != precnt)
//		{
//			PTCHAR* semNameArr = m_pclientcfg->semNamearr;
//
//			PTCHAR cursemName = semNameArr[index];
//
//			SetConsoleColor(FOREGROUND_GREEN);  
//			//cerr<<"[BroadcastMessage] Strategy delay previous semaphore cnt is: "<<precnt<<endl;
//			wprintf(L"[BroadcastMessage]Strategy delay previous semaphore(%s) cnt is: %d\n", cursemName, precnt); 
//
//			SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);  
//		}
//
//
//	}
//
//}